#include <pthread.h>
#include <iostream>
#include <queue>
#include "LockGuard.hpp"
using namespace std;

template <class T>
class BlockQueue
{
public:
    BlockQueue(int capacity = 5)
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_p_cond);
        pthread_cond_destroy(&_c_cond);
    }

    // 生产和消费的基本原则：
    // 1.当共享空间满了后，生产者不允许生产，为空不允许消费
    // 2.何时生产由消费者决定，何时消费由生产者决定

    void Push(const T &in)
    {
        // 加锁
        LockGuard guard(&_mutex);
        while (IsFull() == true)
        {
            // 如果队列满了我们需要阻塞生产者
            pthread_cond_wait(&_p_cond, &_mutex);
        }
        _q.push(in);

        // 只有生产后才能唤醒消费者，也可以制定策略来通知！
        pthread_cond_signal(&_c_cond);
    }
    void Pop(T *out) // 传入输出型参数，退出队列
    {

        LockGuard guard(&_mutex);
        while (Empty() == true) // 这里需要轮询判断，防止伪唤醒
        {
            // 如果队列为空，不允许消费者消费
            pthread_cond_wait(&_c_cond, &_mutex);
        }
        // 退出的数即为队头
        *out = _q.front();
        _q.pop();

        // 只要消费成功可以唤醒生产者
        pthread_cond_signal(&_p_cond);
    }
    bool Empty() { return _q.empty(); }
    bool IsFull() { return _capacity == _q.size(); }

private:
    queue<T> _q;
    int _capacity;

    pthread_mutex_t _mutex;
    // 生产者的条件变量
    pthread_cond_t _p_cond;
    // 消费者的条件变量
    pthread_cond_t _c_cond;
};